package client.model.maze;

import java.util.Observable;
import java.util.Stack;

import org.eclipse.swt.graphics.Point;

import client.model.Model;
import common.CloneMatrix;

public class GameMazeModel extends Observable implements Model {
	private final int[][] originalMazeArray;
	private final int rows;
	private final int columns;
	private final int straightMoveScore;
	private final int diagonalMoveScore;
	private int[][] gameMazeArray;
	private Point currentPosition;
	private int winning;
	private int score;
	
	private Stack<int[][]> undoMoves = new Stack<int[][]>();
	private Stack<Integer> undoScore = new Stack<Integer>();
	
	public GameMazeModel(int[][] board, int straightMoveScore, int diagonalMoveScore) {
		this.rows = board.length;
		this.columns = board[0].length;
		originalMazeArray = CloneMatrix.clone(board);
		this.straightMoveScore = straightMoveScore;
		this.diagonalMoveScore = diagonalMoveScore;
	}
	
	public GameMazeModel(Stack<int[][]> moves, Stack<Integer> scores, int straightMoveScore, int diagonalMoveScore) {
		this(moves.get(0), straightMoveScore, diagonalMoveScore);
		undoMoves = moves;
		undoScore = scores;
	}
	
	@Override
	public String getGameName() {
		return "Maze";
	}
	
	@Override
	public int[][] getBoard() {
		return gameMazeArray;
	}

	public Point getCurrentPosition() {
		return currentPosition;
	}
	
	@Override
	public int getScore() {
		return score;
	}

	@Override
	public int getGameState() {
		return winning;
	}
	
	@Override
	public void initGame() {
		gameMazeArray = CloneMatrix.clone(originalMazeArray);
		setCurrentPosition();
		
		winning = 0;
		score = 0;
		
		undoMoves.clear();
		undoScore.clear();
		
		setChanged();
		notifyObservers();
	}
	
	@Override
	public void updateScore(int sum) {
		score += sum;
	}

	public void setCurrentPosition() {
		for(int i=0; i<rows; i++){
			for(int j=0; j<columns; j++){
				if (gameMazeArray[i][j] == 1) {
					currentPosition = new Point(i, j);
				}
			}
		}
	}
	
	@Override
	public void moveUp() {
		move(currentPosition.x - 1, currentPosition.y, straightMoveScore);
	}
	
	@Override
	public void moveDown() {
		move(currentPosition.x + 1, currentPosition.y, straightMoveScore);
	}
	
	@Override
	public void moveLeft() {
		move(currentPosition.x , currentPosition.y - 1, straightMoveScore);
	}
	
	@Override
	public void moveRight() {
		move(currentPosition.x , currentPosition.y + 1, straightMoveScore);
	}
	
	@Override
	public void moveUpRight(){
		move(currentPosition.x - 1 , currentPosition.y + 1, diagonalMoveScore);
	}
	
	@Override
	public void moveUpLeft(){
		move(currentPosition.x - 1 , currentPosition.y - 1, diagonalMoveScore);
	}
	
	@Override
	public void moveDownRight(){
		move(currentPosition.x + 1 , currentPosition.y + 1, diagonalMoveScore);
	}
	
	@Override
	public void moveDownLeft(){
		move(currentPosition.x + 1 , currentPosition.y - 1, diagonalMoveScore);
	}
	
	private void move(int newRow, int newCol, int moveScore) {
		switch (gameMazeArray[newRow][newCol]) {
			// Wall
			case -1:
				//Don't move
				break;
			//Empty space
			case 0:
				undoMoves.push(CloneMatrix.clone(gameMazeArray));
				undoScore.push(score);
				
				updateScore(moveScore);
				
				gameMazeArray[currentPosition.x][currentPosition.y] = 0;
				gameMazeArray[newRow][newCol] = 1;
				currentPosition.x = newRow;
				currentPosition.y = newCol;
				break;
			//Found the goal	
			case 3:
				updateScore(moveScore);
				
				gameMazeArray[currentPosition.x][currentPosition.y] = 0;
				gameMazeArray[newRow][newCol] = 1;
				currentPosition.x = newRow;
				currentPosition.y = newCol;
				
				winning = 3;
				
				break;
		}
		
		setChanged();
		notifyObservers();
	}

	@Override
	public void undoMove() {
		if (!(undoMoves.isEmpty())) {
			gameMazeArray = undoMoves.pop();
			score = undoScore.pop();
			setCurrentPosition();
			
			setChanged();
			notifyObservers();
		}
	}
	
	@Override
	public void userOption() {
		// No custom mode in this game
	}

	@Override
	@SuppressWarnings("unchecked")
	public Stack<Integer> getUndoScoresStack() {
		Stack<Integer> scores = (Stack<Integer>)undoScore.clone();
		scores.push(score);

		return scores;
	}

	@Override
	@SuppressWarnings("unchecked")
	public Stack<int[][]> getUndoMovesStack() {
		Stack<int[][]> moves = (Stack<int[][]>)undoMoves.clone();
		moves.push(CloneMatrix.clone(gameMazeArray));

		return moves;
	}
}